package org.example.config;

import org.example.batch.BigDataJobExecutionListener;
import org.example.batch.BigDataStudentProcessor;
import org.example.batch.SimpleTasklet;
import org.example.batch.StudentBatchProcessor;
import org.example.domain.Student;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.JdbcPagingItemReader;
import org.springframework.batch.item.database.support.MySqlPagingQueryProvider;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.batch.item.database.Order;
import org.mybatis.spring.batch.MyBatisPagingItemReader;
import org.mybatis.spring.batch.builder.MyBatisPagingItemReaderBuilder;
import org.apache.ibatis.session.SqlSessionFactory;
import org.example.batch.MyBatisStudentProcessor;
import org.example.batch.MyBatisStudentWriter;
import org.example.batch.MyBatisJobExecutionListener;
import org.example.domain.StudentProcessed;

// MyBatis相关的import
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

/**
 * Spring Batch 配置类 - 教学版本
 * 
 * Spring Batch 是一个用于批处理的框架，主要概念包括：
 * 1. Job（作业）：一个批处理作业，包含一个或多个Step
 * 2. Step（步骤）：作业中的一个步骤，可以是Tasklet或Chunk类型
 * 3. Tasklet：简单的任务执行器，适合简单的批处理逻辑
 * 4. Chunk：分块处理，包含Reader、Processor、Writer三个组件
 * 5. Reader：数据读取器，负责从数据源读取数据
 * 6. Processor：数据处理器，负责对读取的数据进行业务处理
 * 7. Writer：数据写入器，负责将处理后的数据写入目标存储
 * 
 * @author example
 */
@Configuration
@EnableBatchProcessing // 启用Spring Batch功能，会自动配置JobRepository、JobLauncher等核心组件
public class BatchConfig {

    /**
     * JobBuilderFactory：用于构建Job的工厂类
     * Spring Batch通过@EnableBatchProcessing自动注入
     */
    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    /**
     * StepBuilderFactory：用于构建Step的工厂类
     * Spring Batch通过@EnableBatchProcessing自动注入
     */
    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    /**
     * 自定义 BatchConfigurer，指定 Spring Batch 使用的数据源
     * 
     * 为什么需要这个配置？
     * - Spring Batch需要数据库来存储作业执行的元数据（如作业状态、执行时间等）
     * - 当项目中有多个数据源时，Spring Batch不知道使用哪个数据源
     * - 通过自定义BatchConfigurer可以明确指定数据源
     * 
     * Spring Batch会自动创建以下元数据表：
     * - BATCH_JOB_INSTANCE：作业实例表
     * - BATCH_JOB_EXECUTION：作业执行表
     * - BATCH_STEP_EXECUTION：步骤执行表
     * - BATCH_JOB_EXECUTION_PARAMS：作业执行参数表
     * - BATCH_JOB_EXECUTION_CONTEXT：作业执行上下文表
     * - BATCH_STEP_EXECUTION_CONTEXT：步骤执行上下文表
     */
    @Bean
    public BatchConfigurer batchConfigurer(@Qualifier("physicalMaster") DataSource dataSource) {
        return new DefaultBatchConfigurer(dataSource);
    }

    /**
     * 创建一个简单的 Step - 基于 Tasklet 模式
     * 
     * Tasklet 模式适用场景：
     * - 简单的批处理任务
     * - 不需要复杂的读取-处理-写入流程
     * - 一次性执行的任务（如数据清理、系统初始化等）
     * 
     * Step 的生命周期：
     * 1. 开始执行（START）
     * 2. 执行Tasklet的execute方法
     * 3. 根据返回值决定是否继续（FINISHED/CONTINUABLE）
     * 4. 完成执行（COMPLETED/FAILED）
     */
    @Bean
    public Step simpleStep() {
        return stepBuilderFactory.get("simpleStep") // 设置Step名称，这个名称会记录在元数据表中
                .tasklet(new SimpleTasklet()) // 指定Tasklet实现
                .build();
    }

    /**
     * 创建一个简单的 Job - 包含一个 Step
     * 
     * Job 的组成：
     * - 一个Job可以包含多个Step
     * - Step按顺序执行（除非使用并行执行）
     * - 如果某个Step失败，整个Job会失败
     * 
     * Job 的执行状态：
     * - STARTING：开始执行
     * - STARTED：已开始
     * - COMPLETED：成功完成
     * - FAILED：执行失败
     * - STOPPED：被停止
     */
    @Bean
    public Job simpleJob() {
        return jobBuilderFactory.get("simpleJob") // 设置Job名称
                .incrementer(new RunIdIncrementer()) // 设置参数递增器，确保每次执行都是新的实例
                .start(simpleStep()) // 设置第一个Step
                .build();
    }

    /**
     * 创建一个数据处理的 Step - 基于 Chunk 模式（读取-处理-写入）
     * 
     * Chunk 模式的工作原理：
     * 1. Reader读取数据，每次读取一条记录
     * 2. 读取达到chunk大小时，开始处理这一批数据
     * 3. Processor对每条记录进行处理（可选）
     * 4. Writer将处理后的一批数据写入目标存储
     * 5. 提交事务，重复上述过程
     * 
     * Chunk 模式的优势：
     * - 内存友好：不会一次性加载全部数据
     * - 事务控制：每个chunk作为一个事务
     * - 可重启：支持失败重启和跳过错误记录
     * - 性能优化：批量写入提高性能
     */
    @Bean
    public Step dataProcessingStep() {
        return stepBuilderFactory.get("dataProcessingStep")
                .<String, String>chunk(10) // 设置chunk大小为10，即每10条记录作为一个批次处理
                .reader(stringItemReader()) // 指定数据读取器
                .processor(stringItemProcessor()) // 指定数据处理器（可选）
                .writer(stringItemWriter()) // 指定数据写入器
                .build();
    }

    /**
     * 字符串数据读取器 - ItemReader接口实现
     * 
     * ItemReader的作用：
     * - 从数据源读取数据（数据库、文件、消息队列等）
     * - 返回单条记录，Spring Batch会反复调用read()方法
     * - 返回null表示没有更多数据
     * 
     * 常见的Reader实现：
     * - JdbcPagingItemReader：分页读取数据库数据
     * - FlatFileItemReader：读取平面文件
     * - JsonItemReader：读取JSON文件
     * - ListItemReader：从内存列表读取（本例使用）
     */
    @Bean
    public ItemReader<String> stringItemReader() {
        List<String> data = new ArrayList<>();
        // 模拟20条数据
        for (int i = 1; i <= 20; i++) {
            data.add("数据项_" + i);
        }
        // ListItemReader会逐个返回列表中的元素
        return new ListItemReader<>(data);
    }

    /**
     * 字符串数据处理器 - ItemProcessor接口实现
     * 
     * ItemProcessor的作用：
     * - 对从Reader读取的数据进行业务处理
     * - 输入类型和输出类型可以不同
     * - 返回null表示过滤掉该记录（不会传给Writer）
     * - 可以抛出异常来标记记录处理失败
     * 
     * 业务处理示例：
     * - 数据转换和格式化
     * - 数据验证和清洗
     * - 业务规则应用
     * - 数据enrichment（数据增强）
     */
    @Bean
    public ItemProcessor<String, String> stringItemProcessor() {
        return item -> {
            // 模拟处理过程的日志输出
            System.out.println("处理数据: " + item);
            
            // 简单的数据处理：添加前缀
            String processedItem = "已处理_" + item;
            
            // 可以在这里添加复杂的业务逻辑
            // 比如：数据验证、格式转换、调用外部服务等
            
            return processedItem;
        };
    }

    /**
     * 字符串数据写入器 - ItemWriter接口实现
     * 
     * ItemWriter的作用：
     * - 将处理后的数据写入目标存储
     * - 接收一个数据列表（chunk中的所有记录）
     * - 支持批量写入，提高性能
     * 
     * 常见的Writer实现：
     * - JdbcBatchItemWriter：批量写入数据库
     * - FlatFileItemWriter：写入平面文件
     * - JsonFileItemWriter：写入JSON文件
     * - CompositeItemWriter：组合多个Writer
     */
    @Bean
    public ItemWriter<String> stringItemWriter() {
        return items -> {
            // items是一个批次的数据列表，大小等于chunk大小
            System.out.println("本批次处理 " + items.size() + " 条记录");
            
            // 批量处理这些数据
            items.forEach(item -> {
                System.out.println("写入数据: " + item);
                // 实际应用中，这里可能是：
                // - 批量插入数据库
                // - 写入文件
                // - 发送到消息队列
                // - 调用外部API等
            });
            
            System.out.println("批次写入完成\n");
        };
    }

    /**
     * 创建一个数据处理的 Job - 演示基础的 Chunk 处理
     * 
     * 这个Job展示了Spring Batch的基本概念：
     * - 使用RunIdIncrementer确保每次执行都是新的Job实例
     * - 包含一个处理字符串数据的Step
     * - 演示Reader-Processor-Writer的完整流程
     */
    @Bean
    public Job dataProcessingJob() {
        return jobBuilderFactory.get("dataProcessingJob")
                .incrementer(new RunIdIncrementer()) // 每次执行生成新的run.id参数
                .start(dataProcessingStep()) // 指定第一个（也是唯一的）Step
                .build();
    }

    /**
     * 学生数据读取器 - 演示复杂对象的处理
     * 
     * 实际应用场景：
     * - 从数据库读取学生信息进行批处理
     * - 从CSV文件导入学生数据
     * - 从外部系统同步学生信息
     * 
     * 这里使用内存数据来模拟，实际项目中通常会：
     * - 使用JdbcPagingItemReader从数据库分页读取
     * - 使用FlatFileItemReader从文件读取
     * - 使用自定义的ItemReader从API获取数据
     */
    @Bean
    public ItemReader<Student> studentItemReader() {
        List<Student> students = new ArrayList<>();
        
        // 模拟5个学生的数据，包含不同年龄以演示业务处理逻辑
        Student student1 = new Student();
        student1.setId(1);
        student1.setName("张三");
        student1.setAge(16); // 未成年
        students.add(student1);
        
        Student student2 = new Student();
        student2.setId(2);
        student2.setName("李四");
        student2.setAge(20); // 成年
        students.add(student2);
        
        Student student3 = new Student();
        student3.setId(3);
        student3.setName("王五");
        student3.setAge(17); // 未成年
        students.add(student3);
        
        Student student4 = new Student();
        student4.setId(4);
        student4.setName("赵六");
        student4.setAge(22); // 成年
        students.add(student4);
        
        Student student5 = new Student();
        student5.setId(5);
        student5.setName("孙七");
        student5.setAge(15); // 未成年
        students.add(student5);
        
        return new ListItemReader<>(students);
    }

    /**
     * 学生数据处理器 - 演示业务逻辑处理
     * 
     * 这个处理器展示了如何对复杂对象进行业务处理：
     * - 根据学生年龄判断是否成年
     * - 在姓名前添加标识前缀
     * - 模拟处理耗时（实际业务中可能涉及数据库查询、外部API调用等）
     * 
     * 使用独立的Processor类的好处：
     * - 代码复用和可测试性
     * - 复杂业务逻辑的封装
     * - 便于单元测试
     */
    @Bean
    public ItemProcessor<Student, Student> studentItemProcessor() {
        return new StudentBatchProcessor();
    }

    /**
     * 学生数据写入器 - 演示批量数据输出
     * 
     * 实际应用场景：
     * - 批量更新数据库中的学生信息
     * - 生成学生报告文件
     * - 发送学生信息到外部系统
     * - 生成统计报表
     * 
     * 批量写入的优势：
     * - 减少数据库连接次数
     * - 提高I/O性能
     * - 支持事务控制
     */
    @Bean
    public ItemWriter<Student> studentItemWriter() {
        return students -> {
            System.out.println("=== 开始批量处理学生数据 ===");
            System.out.println("本批次共 " + students.size() + " 个学生");
            
            students.forEach(student -> {
                System.out.println("保存学生数据: " + student.getName() + 
                    ", 年龄: " + student.getAge() + ", ID: " + student.getId());
                
                // 实际应用中，这里会是：
                // - studentService.updateStudent(student);
                // - studentMapper.batchInsert(students);
                // - 或者其他持久化操作
            });
            
            System.out.println("批次数据处理完成\n");
        };
    }

    /**
     * 学生批处理 Step - 完整的学生数据处理流程
     * 
     * 配置说明：
     * - chunk(5)：每5个学生作为一个批次处理
     * - 为什么选择5？因为我们只有5个测试学生，这样可以看到完整的处理过程
     * - 实际项目中chunk大小通常是100-1000，根据数据量和内存情况调整
     * 
     * 处理流程：
     * 1. Reader读取5个学生（一次性读完）
     * 2. Processor逐个处理每个学生
     * 3. Writer批量写入处理后的5个学生
     * 4. 提交事务
     */
    @Bean
    public Step studentBatchStep() {
        return stepBuilderFactory.get("studentBatchStep")
                .<Student, Student>chunk(5) // 设置chunk大小为5
                .reader(studentItemReader()) // 学生数据读取器
                .processor(studentItemProcessor()) // 学生数据处理器
                .writer(studentItemWriter()) // 学生数据写入器
                .build();
    }

    /**
     * 学生批处理 Job - 完整的学生数据批处理作业
     * 
     * 这个Job演示了一个完整的业务场景：
     * - 从数据源读取学生信息
     * - 根据业务规则处理学生数据
     * - 将处理结果保存到目标存储
     * 
     * 扩展可能性：
     * - 可以添加多个Step处理不同的业务逻辑
     * - 可以添加错误处理和重试机制
     * - 可以添加监听器来监控处理进度
     * - 可以添加条件判断来控制Step的执行流程
     */
    @Bean
    public Job studentBatchJob() {
        return jobBuilderFactory.get("studentBatchJob")
                .incrementer(new RunIdIncrementer()) // 确保每次执行都是新的实例
                .start(studentBatchStep()) // 开始执行学生批处理Step
                .build();
    }

    // ==================== 大数据量批处理配置 ====================

    /**
     * 大数据量学生数据读取器 - 基于数据库分页查询
     * 
     * JdbcPagingItemReader 是 Spring Batch 提供的高性能数据库读取器
     * 
     * 分页读取的优势：
     * 1. 内存友好：不会一次性加载全部数据到内存
     * 2. 性能优化：利用数据库索引进行高效查询
     * 3. 可重启：支持失败重启，从中断点继续处理
     * 4. 并发安全：支持多线程并发读取（配置不同的分区）
     * 
     * 配置要点：
     * - pageSize：每页读取的记录数，影响内存使用和查询性能
     * - fetchSize：JDBC 获取大小，优化网络传输
     * - sortKeys：排序字段，确保数据读取的一致性和可重复性
     * 
     * 实际应用场景：
     * - 百万级别的数据ETL处理
     * - 定期的数据清洗和同步任务
     * - 大批量的数据迁移
     * - 报表数据的批量生成
     * 
     * 性能调优建议：
     * - pageSize设置为100-1000之间，根据记录大小调整
     * - 确保排序字段有索引
     * - 监控数据库连接池的使用情况
     * - 根据服务器内存调整chunk大小
     */
    @Bean
    public ItemReader<Student> bigDataStudentReader(@Qualifier("physicalMaster") DataSource dataSource) 
            throws Exception {
        
        // 创建JdbcPagingItemReader
        JdbcPagingItemReader<Student> reader = new JdbcPagingItemReader<>();
        
        // 设置数据源
        reader.setDataSource(dataSource);
        
        // 设置每页读取的记录数
        // 这个值需要根据实际情况调整：
        // - 记录越大，pageSize应该越小
        // - 内存越小，pageSize应该越小
        // - 一般设置为100-1000之间
        reader.setPageSize(500);
        
        // 设置JDBC fetch size，优化网络传输
        // 通常设置为pageSize的1-2倍
        reader.setFetchSize(1000);
        
        // 设置行映射器，将数据库记录映射为Java对象
        // BeanPropertyRowMapper会自动根据列名映射到对象属性
        reader.setRowMapper(new BeanPropertyRowMapper<>(Student.class));
        
        // 创建MySQL分页查询提供者
        MySqlPagingQueryProvider queryProvider = new MySqlPagingQueryProvider();
        
        // 设置基础查询SQL（不包含ORDER BY和LIMIT）
        // 注意：不要在这里添加ORDER BY子句，排序由sortKeys控制
        queryProvider.setSelectClause("SELECT id, name, age, gender, email");
        queryProvider.setFromClause("FROM student");
        
        // 可以添加WHERE条件进行数据过滤
        // 例如：只处理特定年龄段的学生
        // queryProvider.setWhereClause("WHERE age BETWEEN 18 AND 60");
        
        // 设置排序字段，这个非常重要！
        // 排序确保：
        // 1. 数据读取的一致性（相同查询总是返回相同顺序的结果）
        // 2. 可重启性（重启后能从正确的位置继续）
        // 3. 避免重复或遗漏数据
        Map<String, Order> sortKeys = new HashMap<>();
        sortKeys.put("id", Order.ASCENDING); // 按ID升序排列
        queryProvider.setSortKeys(sortKeys);
        
        // 设置查询提供者
        reader.setQueryProvider(queryProvider);
        
        // 设置reader名称，便于监控和调试
        reader.setName("bigDataStudentReader");
        
        // 初始化reader
        reader.afterPropertiesSet();
        
        return reader;
    }

    /**
     * 大数据量学生数据处理器
     * 
     * 使用专门的处理器来处理大数据量场景：
     * - 优化的处理逻辑，减少处理时间
     * - 内置的统计和监控功能
     * - 更好的错误处理和容错能力
     */
    @Bean
    public ItemProcessor<Student, Student> bigDataStudentProcessor() {
        return new BigDataStudentProcessor();
    }

    /**
     * 大数据量学生数据写入器
     * 
     * 在大数据量场景下，写入器需要特别注意：
     * 1. 批量写入：减少数据库交互次数
     * 2. 事务控制：合理的事务大小
     * 3. 性能监控：记录写入性能指标
     * 4. 错误处理：处理写入失败的情况
     * 
     * 实际应用中可能的写入目标：
     * - 数据库表：使用JdbcBatchItemWriter
     * - 文件：使用FlatFileItemWriter
     * - 消息队列：使用JmsItemWriter
     * - 外部API：使用自定义Writer
     */
    @Bean
    public ItemWriter<Student> bigDataStudentWriter() {
        return students -> {
            // 记录批次开始时间，用于性能监控
            long batchStartTime = System.currentTimeMillis();
            
            System.out.println("\n=== 开始批量写入学生数据 ===");
            System.out.println("批次大小: " + students.size() + " 条记录");
            
            // 统计各类学生数量
            long adultCount = students.stream().filter(s -> 
                s.getName() != null && s.getName().contains("[成年]")).count();
            long youthCount = students.stream().filter(s -> 
                s.getName() != null && s.getName().contains("[青年]")).count();
            long minorCount = students.stream().filter(s -> 
                s.getName() != null && s.getName().contains("[未成年]")).count();
            
            System.out.println("数据分布 - 成年: " + adultCount + 
                             ", 青年: " + youthCount + 
                             ", 未成年: " + minorCount);
            
            // 模拟批量写入处理
            // 在实际应用中，这里会是：
            // - jdbcBatchItemWriter.write(students);
            // - 或者自定义的批量插入逻辑
            
            for (int i = 0; i < students.size(); i++) {
                Student student = students.get(i);
                // 这里只是示例输出，实际应用中会写入数据库
                if (i < 3 || i >= students.size() - 3) { // 只显示前3个和后3个
                    System.out.println("写入学生: ID=" + student.getId() + 
                                     ", 姓名=" + student.getName() + 
                                     ", 年龄=" + student.getAge() + 
                                     ", 性别=" + student.getGender());
                } else if (i == 3) {
                    System.out.println("... (省略中间 " + (students.size() - 6) + " 条记录) ...");
                }
            }
            
            // 计算写入性能
            long batchEndTime = System.currentTimeMillis();
            long batchDuration = batchEndTime - batchStartTime;
            double recordsPerSecond = students.size() * 1000.0 / batchDuration;
            
            System.out.println("批次写入完成！耗时: " + batchDuration + " ms");
            System.out.println("写入性能: " + String.format("%.2f", recordsPerSecond) + " 记录/秒");
            System.out.println("=== 批量写入完成 ===\n");
        };
    }

    /**
     * 任务执行监听器
     * 
     * 在大数据量处理中，监听器的作用特别重要：
     * - 任务开始前的准备工作
     * - 任务执行过程中的监控
     * - 任务完成后的统计和清理
     */
    @Bean
    public BigDataJobExecutionListener bigDataJobExecutionListener() {
        return new BigDataJobExecutionListener();
    }

    /**
     * 大数据量学生批处理 Step - 生产级别的配置
     * 
     * 大数据量处理的关键配置：
     * 1. chunk大小：平衡内存使用和事务性能
     * 2. 容错配置：跳过策略和重试机制
     * 3. 监听器：监控执行进度和性能
     * 4. 事务配置：合理的事务边界
     * 
     * chunk大小选择原则：
     * - 太小：频繁的事务提交，性能较差
     * - 太大：内存占用高，事务时间长
     * - 推荐：100-1000之间，根据记录大小和内存调整
     * 
     * 在本示例中：
     * - pageSize=500：每次从数据库读取500条记录
     * - chunkSize=100：每100条记录提交一次事务
     * - 这样可以在内存使用和性能之间取得平衡
     */
    @Bean
    public Step bigDataStudentStep(@Qualifier("physicalMaster") DataSource dataSource) throws Exception {
        return stepBuilderFactory.get("bigDataStudentStep")
                .<Student, Student>chunk(100) // 设置chunk大小为100
                .reader(bigDataStudentReader(dataSource)) // 数据库分页读取器
                .processor(bigDataStudentProcessor()) // 大数据量处理器
                .writer(bigDataStudentWriter()) // 批量写入器
                
                // 容错配置：跳过异常记录，避免个别坏数据影响整个任务
                .faultTolerant() // 启用容错
                .skipLimit(10) // 最多跳过10条记录
                .skip(Exception.class) // 跳过所有异常（实际应用中应该更具体）
                
                // 可以添加重试配置
                // .retryLimit(3) // 重试3次
                // .retry(DeadlockLoserDataAccessException.class) // 重试死锁异常
                
                .build();
    }

    /**
     * 大数据量学生批处理 Job - 生产级别的任务配置
     * 
     * 这个Job展示了处理大数据量的最佳实践：
     * - 分页读取：避免内存溢出
     * - 批量处理：提高处理性能  
     * - 容错机制：提高任务稳定性
     * - 监控统计：便于性能调优和问题排查
     * 
     * 适用场景：
     * - 数据库中有大量学生记录需要批量处理
     * - 定期的数据清洗和标准化任务
     * - 数据迁移和同步作业
     * - 报表数据的生成和统计
     * 
     * 扩展建议：
     * - 添加分区处理：支持并行执行
     * - 集成监控系统：实时监控任务状态
     * - 添加数据质量检查：确保处理结果的正确性
     * - 配置调度系统：实现定时自动执行
     */
    @Bean
    public Job bigDataStudentJob(@Qualifier("physicalMaster") DataSource dataSource) throws Exception {
        return jobBuilderFactory.get("bigDataStudentJob")
                .incrementer(new RunIdIncrementer()) // 确保每次执行都是新的实例
                .listener(bigDataJobExecutionListener()) // 添加任务执行监听器
                .start(bigDataStudentStep(dataSource)) // 开始执行大数据量处理Step
                .build();
    }

    // ========================================
    // MyBatis 版本的批处理配置 - 新增功能
    // ========================================

    /**
     * 为Spring Batch专门配置的SqlSessionFactory
     * 
     * 🎯 作用：
     * - 为MyBatis批处理任务提供数据库连接
     * - 指定使用主数据源（physicalMaster）
     * - 配置MyBatis的映射文件和类型别名
     * 
     * 🔧 配置说明：
     * - 数据源：physicalMaster（避免多数据源冲突）
     * - 映射文件：classpath*:mapper/*.xml
     * - 类型别名：自动扫描domain包
     * - 下划线转驼峰：启用自动映射
     * 
     * 📊 与项目中其他SqlSessionFactory的区别：
     * - 专门为Spring Batch服务
     * - 明确指定数据源，避免动态数据源的复杂性
     * - 优化的配置，适合批处理场景
     * 
     * @param dataSource 主数据源
     * @return 配置好的SqlSessionFactory
     * @throws Exception 配置异常
     */
    @Bean("batchSqlSessionFactory")
    public SqlSessionFactory batchSqlSessionFactory(@Qualifier("physicalMaster") DataSource dataSource) 
            throws Exception {
        
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        
        // 设置数据源 - 明确使用主数据源
        sessionFactory.setDataSource(dataSource);
        
        // 设置MyBatis映射文件位置
        // 包括StudentProcessedMapper.xml等批处理相关的映射文件
        sessionFactory.setMapperLocations(
            new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml")
        );
        
        // 设置类型别名包
        // 自动为domain包下的类创建别名，如Student、StudentProcessed等
        sessionFactory.setTypeAliasesPackage("org.example.domain");
        
        // MyBatis核心配置
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        
        // 启用下划线到驼峰的自动映射
        // 例如：student_name -> studentName
        configuration.setMapUnderscoreToCamelCase(true);
        
        // 启用自动映射
        configuration.setAutoMappingBehavior(org.apache.ibatis.session.AutoMappingBehavior.FULL);
        
        // 设置默认执行器类型为BATCH（批处理优化）
        configuration.setDefaultExecutorType(org.apache.ibatis.session.ExecutorType.BATCH);
        
        // 设置JDBC类型为null值的处理
        configuration.setJdbcTypeForNull(org.apache.ibatis.type.JdbcType.NULL);
        
        // 启用缓存（适度使用，批处理场景下通常关闭二级缓存）
        configuration.setCacheEnabled(false);
        
        // 设置延迟加载（批处理场景通常不需要）
        configuration.setLazyLoadingEnabled(false);
        
        sessionFactory.setConfiguration(configuration);
        
        return sessionFactory.getObject();
    }

    /**
     * MyBatis分页读取器 - 使用MyBatis进行数据读取
     * 
     * MyBatisPagingItemReader的优势：
     * 1. 充分利用MyBatis的强大功能（动态SQL、复杂映射等）
     * 2. 可以使用XML映射文件定义复杂查询
     * 3. 支持MyBatis的缓存机制
     * 4. 更好的SQL调优和监控支持
     * 
     * 与JdbcPagingItemReader的对比：
     * - JdbcPagingItemReader：使用JDBC，性能略好，配置简单
     * - MyBatisPagingItemReader：使用MyBatis，功能更强，SQL更灵活
     * 
     * @param sqlSessionFactory MyBatis的SqlSessionFactory
     * @return MyBatis分页读取器
     */
    @Bean
    public MyBatisPagingItemReader<Student> myBatisStudentReader(@Qualifier("batchSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new MyBatisPagingItemReaderBuilder<Student>()
                .sqlSessionFactory(sqlSessionFactory)
                
                // 指定MyBatis映射器中的查询方法
                // 对应StudentProcessedMapper.selectStudentsForBatch方法
                .queryId("org.example.mapper.StudentProcessedMapper.selectStudentsForBatch")
                
                // 设置分页大小
                // 这个值会传递给MyBatis查询，用于LIMIT子句
                .pageSize(500)

                
                // 设置查询参数（可选）
                // 这些参数会传递给MyBatis查询方法
                .parameterValues(new HashMap<String, Object>() {{
                    // 可以添加查询条件，例如：
                    // put("minAge", 18);
                    // put("maxAge", 65);
                    // put("gender", "M");
                }})
                
                .build();
    }

    /**
     * MyBatis版本的学生数据处理器
     * 
     * 这个处理器专门为MyBatis集成场景设计：
     * - 输入：原始的Student对象（从student表读取）
     * - 输出：处理后的StudentProcessed对象（写入student_processed表）
     * - 包含数据质量评分、分类处理等增强功能
     */
    @Bean
    public ItemProcessor<Student, StudentProcessed> myBatisStudentProcessor() {
        return new MyBatisStudentProcessor();
    }

    /**
     * MyBatis版本的学生数据写入器
     * 
     * 使用MyBatis进行批量数据写入：
     * - 利用MyBatis的批量插入功能
     * - 支持复杂的数据映射和类型转换
     * - 提供详细的写入统计和监控
     */
    @Bean
    public ItemWriter<StudentProcessed> myBatisStudentWriter() {
        return new MyBatisStudentWriter();
    }

    /**
     * MyBatis版本的任务执行监听器
     * 
     * 专门为MyBatis集成场景设计的监听器：
     * - 监控MyBatis批处理的执行过程
     * - 提供数据库级别的统计查询
     * - 输出MyBatis特有的性能和功能信息
     */
    @Bean
    public MyBatisJobExecutionListener myBatisJobExecutionListener() {
        return new MyBatisJobExecutionListener();
    }

    /**
     * MyBatis版本的批处理Step
     * 
     * 这个Step展示了Spring Batch与MyBatis的完美集成：
     * 
     * 技术栈：
     * - 数据读取：MyBatisPagingItemReader + XML映射文件
     * - 数据处理：自定义Processor（Student -> StudentProcessed）
     * - 数据写入：MyBatis批量插入
     * - 容错机制：跳过异常记录，确保任务稳定性
     * 
     * 配置特点：
     * - chunk大小：100条记录/事务
     * - 容错限制：最多跳过20条异常记录
     * - 事务边界：每个chunk一个事务
     * 
     * 适用场景：
     * - 需要复杂SQL查询的批处理
     * - 数据结构转换（表到表的映射）
     * - 数据质量提升和标准化
     * - 业务数据的ETL处理
     */
    @Bean
    public Step myBatisStudentStep(@Qualifier("batchSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return stepBuilderFactory.get("myBatisStudentStep")
                // 设置chunk类型和大小
                // <Student, StudentProcessed> 表示输入类型和输出类型
                .<Student, StudentProcessed>chunk(100)
                
                // 配置三大组件
                .reader(myBatisStudentReader(sqlSessionFactory))
                .processor(myBatisStudentProcessor())
                .writer(myBatisStudentWriter())
                
                // 容错配置
                .faultTolerant()
                .skipLimit(20) // MyBatis场景下允许更多跳过，因为数据结构转换可能更复杂
                .skip(Exception.class)
                
                // 可以添加监听器监控Step执行
                // .listener(stepExecutionListener())
                
                .build();
    }

    /**
     * MyBatis版本的批处理Job
     * 
     * 这个Job是Spring Batch + MyBatis集成的完整示例：
     * 
     * 🎯 核心特色：
     * - 使用MyBatis XML映射文件定义复杂查询
     * - 支持动态SQL和条件查询
     * - 充分利用MyBatis的对象映射能力
     * - 提供数据库级别的统计和监控
     * 
     * 📊 处理流程：
     * 1. 从student表读取原始数据（MyBatis分页查询）
     * 2. 执行业务处理和数据质量评分（自定义Processor）
     * 3. 批量写入student_processed表（MyBatis批量插入）
     * 4. 生成详细的处理报告和统计信息
     * 
     * 🚀 性能优化：
     * - 分页读取避免内存溢出
     * - 批量写入提高数据库性能
     * - 合理的事务边界控制
     * - MyBatis缓存机制利用
     * 
     * 📈 监控功能：
     * - 实时处理进度显示
     * - 数据质量统计分析
     * - 性能指标监控
     * - 数据库统计查询
     * 
     * 🔧 扩展建议：
     * - 添加数据分区处理（并行执行）
     * - 集成分布式任务调度
     * - 添加数据血缘追踪
     * - 实现增量处理机制
     */
    @Bean
    public Job myBatisStudentJob(@Qualifier("batchSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return jobBuilderFactory.get("myBatisStudentJob")
                .incrementer(new RunIdIncrementer())
                .listener(myBatisJobExecutionListener())
                .start(myBatisStudentStep(sqlSessionFactory))
                .build();
    }
} 